// Copyright Druid Mechanics


#include "AbilitySystem/Abilities/AuraBeamSpell.h"

#include "AbilitySystem/AuraAbilitySystemLibrary.h"
#include "GameFramework/Character.h"
#include "Kismet/KismetSystemLibrary.h"

void UAuraBeamSpell::StoreMouseDataInfo(const FHitResult& HitResult)
{
	if (HitResult.bBlockingHit)
	{
		MouseHitLocation = HitResult.ImpactPoint;
		MouseHitActor = HitResult.GetActor();
	}
	else
	{
		/**
		 *CancelAbility: 
		 * 销毁instanced-per-execution类型的GA。Instance-per-actor类型的GA被重置。
		 * 任何激活的GAST(ability state tasks)收到OnAbilityStateInterrupted事件。
		 */
		/**
		 * CurrentSpecHandle: 当前实例的GASpec句柄。
		 */
		/**
		 * CurrentActorInfo:
		 * 这是共享的、缓存的信息关于我们使用的一些东西。
		 * 例如: Actor*, MovementComponent*, AnimInstance等。
		 * 这有望在每个角色身上仅分配一次，并由多种能力共享。
		 * 实际的结构体可能会因每个游戏而被重写，以包含特定于该游戏的数据。
		 * 例如: 子类可能会重写自己的FMyGameAbilityActorInfo。
		 */
		/**
		 * CurrentActivationInfo: 这是具体的关于实例化GA的信息。例如: 是否在预测，创建，确认等。
		 */
		CancelAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true);
	}
}

void UAuraBeamSpell::StoreOwnerVariables()
{
	if (CurrentActorInfo)
	{
		OwnerPlayerController = CurrentActorInfo->PlayerController.Get();
		OwnerCharacter = Cast<ACharacter>(CurrentActorInfo->AvatarActor);
	}
}

void UAuraBeamSpell::TraceFirstTarget(const FVector& BeamTargetLocation)
{
	check(OwnerCharacter);
	if (OwnerCharacter->Implements<UCombatInterface>())
	{
		if (USkeletalMeshComponent* Weapon = ICombatInterface::Execute_GetWeapon(OwnerCharacter))
		{
			const FVector SocketLocation = Weapon->GetSocketLocation(FName("TipSocket"));
			TArray<AActor*> ActorsToIgnore;
			ActorsToIgnore.Add(OwnerCharacter);
			FHitResult HitResult;
			/**
			 * SphereTraceSingle: 
			 * 沿着给定线扫过球体并返回遭遇的第一个Block碰撞。
			 * 这个追踪寻找回复给定TraceChannel回复的对象。
			 * ETraceTypeQuery: 这里的参数对应的是自定义的碰撞轨道参数。
			 * NOTE: 本质上这个函数就是射线检测，只不过进行了封装。
			 * 针对ETraceTypeQuery类型解释，这里是自定义射线类型的枚举，UE默认有ECC_Visibility和ECC_Camera两个射线轨道，在UE初始化时，数组存储，根据的是枚举值可以转化为整数的特性。
			 * 所以，有对应ETraceTypeQuery::TraceTypeQuery1 => ECC_Visibility; ETraceTypeQuery::TraceTypeQuery2 => ECC_Camera。另外还有EObjectTypeQuery，他也是一个对应。
			 * EObjectTypeQuery::ObjectTypeQuery1 => ECC_WorldStatic; EObjectTypeQuery::ObjectTypeQuery2 => ECC_WorldDynamic。等等。
			 * 还有自定义的碰撞轨道啥的，也是直接在后面添加。
			 */
			UKismetSystemLibrary::SphereTraceSingle(OwnerCharacter, SocketLocation, BeamTargetLocation, 10.f,
			                                        ETraceTypeQuery::TraceTypeQuery1,
			                                        false, ActorsToIgnore, EDrawDebugTrace::None, HitResult, true);
			if (HitResult.bBlockingHit)
			{
				MouseHitLocation = HitResult.ImpactPoint;
				MouseHitActor = HitResult.GetActor();
			}
		}
	}
	if(ICombatInterface* CombatInterface = Cast<ICombatInterface>(MouseHitActor))
	{
		if(!CombatInterface->GetOnDeathDelegate().IsAlreadyBound(this, &UAuraBeamSpell::PrimaryTargetDied))
		{
			CombatInterface->GetOnDeathDelegate().AddDynamic(this, &UAuraBeamSpell::PrimaryTargetDied);
		}
	}
}

void UAuraBeamSpell::StoreAdditionalTargets(TArray<AActor*>& OutAdditionalTargets)
{
	TArray<AActor*> ActorsToIgnore;
	ActorsToIgnore.Add(GetAvatarActorFromActorInfo());
	ActorsToIgnore.Add(MouseHitActor);
	TArray<AActor*> OverlappingActors;
	UAuraAbilitySystemLibrary::GetLivePlayersWithinRadius(GetAvatarActorFromActorInfo(), OverlappingActors,
	                                                      ActorsToIgnore, 850.f, MouseHitActor->GetActorLocation());
	int32 NumAdditionalTargets = FMath::Min(GetAbilityLevel() - 1, MaxNumShockTargets);
	UAuraAbilitySystemLibrary::GetClosestTargets(NumAdditionalTargets, OverlappingActors, OutAdditionalTargets,
	                                             MouseHitActor->GetActorLocation());
	for(AActor* Target : OutAdditionalTargets)
	{
		if(ICombatInterface* CombatInterface = Cast<ICombatInterface>(MouseHitActor))
		{
			if(!CombatInterface->GetOnDeathDelegate().IsAlreadyBound(this, &UAuraBeamSpell::AdditionalTargetDied))
			{
				CombatInterface->GetOnDeathDelegate().AddDynamic(this, &UAuraBeamSpell::AdditionalTargetDied);
			}
		}
	}
}
